﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows;
using KewlStuff.Misc;
using MovieBuddy.Properties;
using MovieBuddy.Views.MovieListView;
using MovieModel.Model;

namespace MovieBuddy.Dialogs
{
    /// <summary>
    /// Interaction logic for PlaylistDialog.xaml
    /// </summary>
    public partial class PlaylistDialog
    {
        public static PlaylistDialog Instance = new PlaylistDialog();

        private readonly Dictionary<Movie, Process> playingMovies = new Dictionary<Movie, Process>();
        private Movie currentMovie;

        private PlaylistDialog()
        {
            FormSizeSaver.RegisterForm(this, () => Settings.Default.PlaylistWindowSettings,
                                       s =>
                                       {
                                           Settings.Default.PlaylistWindowSettings = s;
                                           Settings.Default.Save();
                                       });

            InitializeComponent();
            movieList.OrderSelector = null;
            movieList.SelectionChanged += MovieListSelectionChanged;
            playButton.IsEnabled = false;
            removeButton.IsEnabled = false;
        }

        void MovieListSelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            var items = movieList.SelectedItems;
            playButton.IsEnabled = items.Length == 1;
            removeButton.IsEnabled = items.Length > 0;
        }

        /// <summary>
        /// Plays the movie.  Makes it the only thing in the playlist.  If this movie
        /// is already playing it flashes that window at the user, but does not start another instance.
        /// </summary>
        /// <param name="movies"></param>
        public void PlayMovies(Movie[] movies)
        {
            movieList.Clear();
            EnqueueMovies(movies);
            StartMovie(0);
        }

        public void EnqueueMovies(Movie[] movies)
        {
            foreach (var movie in movies)
            {
                var item = movieList.AddMovie(movie);
                item.MouseDoubleClick += (s, e) => StartMovie(item);
            }
        }

        public void RemoveMovie(Movie movie)
        {
            movieList.RemoveMovie(movie);
        }

        private void StartMovie(MovieListItem item)
        {
            int index = 0;
            foreach (var i in movieList.listView.Items.Cast<MovieListItem>())
            {
// ReSharper disable once PossibleUnintendedReferenceComparison
                if (i == item)
                {
                    StartMovie(index);
                    return;
                }
                index++;
            }
        }

        private void StartMovie(int index)
        {
            foreach (var i in movieList.listView.Items.Cast<MovieListItem>())
            {
                if (i.Bold) i.Bold = false;
            }
            var item = (MovieListItem) movieList.listView.Items[index];
            var movie = item.Movie;
            currentMovie = movie;
            item.Bold = true;

            bool useVlc = !string.IsNullOrEmpty(Settings.Default.VLCLocation) &&
                          Settings.Default.VLCFiles.Split(';').Select(s => s.Trim()).Any(
                              s => s.Length > 0 && FindFilesPatternToRegex.Convert(s).IsMatch(movie.MovieFile.Name));

            ProcessStartInfo starter;
            if (useVlc)
            {
                starter = new ProcessStartInfo(Settings.Default.VLCLocation, string.Join(" ", ForCommandLine(movie.MovieFile.FullName), "--play-and-exit", "--fullscreen"));
            }
            else
            {
                starter = new ProcessStartInfo(movie.MovieFile.FullName);
            }

            var p = Process.Start(starter);

// ReSharper disable once ConditionIsAlwaysTrueOrFalse
// ReSharper disable once HeuristicUnreachableCode
            if (p == null) return; // the OS can do crazy things
            playingMovies[movie] = p;
            var wt = new WaiterThread(item, p, this);
            var t = new Thread(wt.Run);
            t.IsBackground = true; // movie buddy can close before the media player is closed
            t.Start();
        }

        private class WaiterThread
        {
            private readonly MovieListItem movieItem;
            private readonly Process process;
            private readonly PlaylistDialog dialog;
            internal WaiterThread(MovieListItem movieItem, Process process, PlaylistDialog dialog)
            {
                this.movieItem = movieItem;
                this.process = process;
                this.dialog = dialog;
            }
            
            internal void Run()
            {
                process.WaitForExit();
                dialog.playingMovies.Remove(movieItem.Movie);
                movieItem.Dispatcher.Invoke((Action)(() => movieItem.Bold = false));
                if (dialog.currentMovie == movieItem.Movie)
                {
                    int index = 0;
                    foreach (var i in dialog.movieList.listView.Items.Cast<MovieListItem>())
                    {
// ReSharper disable once PossibleUnintendedReferenceComparison
                        if (i == movieItem)
                        {
                            if (dialog.movieList.listView.Items.Count > index + 1)
                                dialog.StartMovie(index + 1);
                            return;
                        }
                        index++;
                    }
                }
            }
        }

        /// <summary>
        /// makes a string safe to put on the command line.  This sucks.
        /// </summary>
        /// <param name="argument"></param>
        /// <returns></returns>
        private static string ForCommandLine(string argument)
        {
            return string.Format(@"""{0}""", argument.Replace(@"""", @""""""));
        }

        private void ClosingHandler(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (allowClose) return;
            Hide();
            e.Cancel = true;
        }

        private bool allowClose;
        public void RealClose()
        {
            allowClose = true;
            Close();
        }

        private void RemoveButtonClick(object sender, RoutedEventArgs e)
        {
            var items = movieList.SelectedItems;
            foreach (var item in items)
            {
                movieList.RemoveItem(item);
            }
        }

        private void PlayButtonClicked(object sender, RoutedEventArgs e)
        {
            StartMovie(movieList.SelectedItems[0]);
        }
    }
}
